﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Emulator.GameBoy.Memory;
using Emulator.GameBoy.CPU;
using System.Threading;
using System.Diagnostics;

namespace Emulator.GameBoy
{
    public class GameBoyConsole
    {
        Stopwatch xTimer;
        public event Action<uint[]> VideoFrameUpdateEvent = (parameter => { });
        object lockObj = new object();

        GameBoyMemory xMemory = null;
        GameBoyCPU xCPU = null;
        GameboyGPU xGPU = null;
#if DEBUG
        StringBuilder xConsoleErrorContainer = new StringBuilder();
        public bool DebugFlag = false;
        public event Action<string> CurrentInstruction;
        ManualResetEvent xCPUManual = new ManualResetEvent(false);
        ManualResetEvent xGPUManual = new ManualResetEvent(false);

        //string xErrorPrifex = "GameBoy Console Exception:";
#endif
        Thread xCPUThread,xGPUThread;

        public GameBoyConsole()
        {
            xTimer = new Stopwatch();
            xMemory = new GameBoyMemory();
            xCPU = new GameBoyCPU(xMemory);
            xGPU = new GameboyGPU(xMemory);
            xGPU.FlushFrameFinishEvent += (obj => VideoFrameUpdateEvent(obj));
        }

        public void LoadRom(byte[] aRomContent)
        {
            xMemory.ResetMemory(aRomContent);
            xCPU.ResetCPU();

//            xGPUThread = new Thread(paremeter=>{
//                xGPU.FlushFrameData(xMemory.MemoryData,xCPU);
//            });
//            xCPUThread = new Thread(parameter =>
//            {
//#if DEBUG                
//                if (DebugFlag)
//                {
//                    Stop();
//                    DebugFlag = false;
//                    return;
//                }
//#endif
//                xCPU.Run(xMemory,((Stopwatch)parameter).Elapsed);
//            });
//#if DEBUG
//            xCPU.RunningInstructionEvent += aInstructionName => { if (CurrentInstruction != null)CurrentInstruction(aInstructionName); };
//#endif
        }

        public void Stop()
        {
        }

        public void Start()
        {
            ThreadPool.QueueUserWorkItem(obj =>
            {
                xTimer.Start();
                while (true)
                {
                    xCPU.Run(xMemory, xTimer.Elapsed);
                    xGPU.FlushFrameData(xMemory.MemoryData,xCPU);
                }
            });
        }        

        public void EnableDisplay()
        {
            xMemory.Write8(0xFF40,(byte)(xMemory.Read8(0xFF40)|0xa3));//10100011
        }

        public void DisableDisplay()
        {
            xMemory.Write8(0xFF40, (byte)(xMemory.Read8(0xFF40) & 0x5c));//01011100
        }

        public void OutputFrame(UInt32[] aVRAM)
        {
            DisableDisplay();
#if DEBUG   
            VideoFrameUpdateEvent(aVRAM);
#endif
            EnableDisplay();
        }

        public void SetTimerFrequence(int aInterval)
        {
        }

#if DEBUG
        public string OutputCPUError()
        {
            return xCPU.OutputCPUError();
        }
        public string OutputVRAM()
        {
            return xGPU.OutputVRAMString();
        }
        public string OutputCPUInfo()
        {
            return xCPU.OutputCPUDebugData();
        }
        public string OutputGPUInfo()
        {
            return xMemory.OutputGPURegister();
        }
        public string OutputMemory()
        {
            return xMemory.OutputRAMString();
        }
#endif
    }
}
